package org.tgcloud.group.service.app;

import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.stereotype.Service;
import org.tgcloud.group.core.APPResultMessage;
import org.tgcloud.group.core.RedisUtil;
import org.tgcloud.group.core.ResultMessage;
import org.tgcloud.group.core.StringUtil;
import org.tgcloud.group.core.mapUtils.DistanceCalculator;
import org.tgcloud.group.core.mapUtils.GeoUtils;
import org.tgcloud.group.entity.MediaType;
import org.tgcloud.group.repository.MediaMapper;
import org.tgcloud.group.repository.MediaTypeMapper;
import org.tgcloud.group.service.manager.AsyncService;
import org.tgcloud.group.service.manager.MediaManageService;
import org.tgcloud.group.web.controller.dto.media.MediaAppBriefDTO;
import org.tgcloud.group.web.controller.dto.media.MediaDTO;
import org.tgcloud.group.web.controller.dto.media.MediaExportMsgDTO;
import org.tgcloud.group.web.controller.dto.media.MediaOtherMsgDTO;
import org.tgcloud.group.web.controller.req.media.*;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.*;

/**
 * @Author: zhy
 * @Created: 2025-02-13-下午1:43
 * @Description:
 */
@Service
public class MediaService {

    private static final String showPassword = "177513";

    @Autowired
    private MediaManageService mediaManageService;

    @Autowired
    private MediaTypeMapper mediaTypeMapper;
    @Autowired
    private MediaMapper mediaMapper;
    @Autowired
    private GeoUtils geoUtils;
    @Autowired
    private AsyncService asyncService;

    public APPResultMessage getMediaAppList(MediaReq req) {
        Integer radiusKm = DistanceCalculator.getRadiusKm(req.getNeLat(), req.getNeLng(), req.getSwLat(), req.getSwLng());
        List<Integer> mediaIdList = getMediaIdList(Double.parseDouble(req.getLon()), Double.parseDouble(req.getLat()), radiusKm);
        if (mediaIdList == null || mediaIdList.size() == 0) {
            return APPResultMessage.getSuccessMessage();
        }
        String[] mediaTypes = null;
        if (StringUtil.isNotBlank(req.getMediaTypes())){
            mediaTypes = req.getMediaTypes().split(",");
        }
        if (req.getPageNum() != null && req.getPageSize() != null){
            PageHelper.startPage(req.getPageNum(), req.getPageSize());
            List<MediaAppBriefDTO> dtos = mediaMapper.selectAppBriefList(mediaIdList, mediaTypes);
            PageInfo<MediaAppBriefDTO> tables = new PageInfo<>(dtos);
            ResultMessage result = ResultMessage.builder().setList(tables.getList()).setAllPageNumber(tables.getPages()).setCount(tables.getTotal());
            APPResultMessage<Object, MediaAppBriefDTO> message = APPResultMessage.getSuccessMessage();
            message.setResult(result);
            return message;
        }else {
            List<MediaAppBriefDTO> dtos = mediaMapper.selectAppBriefList(mediaIdList, mediaTypes);
            ResultMessage result = ResultMessage.builder().setList(dtos);
            APPResultMessage<Object, MediaAppBriefDTO> message = APPResultMessage.getSuccessMessage();
            message.setResult(result);
            return message;
        }
    }

    public APPResultMessage getMediaTypeAppList() {
        List<MediaType> mediaTypes = mediaTypeMapper.selectAppList();
        Map dataMap = new HashMap();
        JSONObject classTableByApiModelProperty = mediaManageService.getClassTableByApiModelProperty(MediaOtherMsgDTO.class.getDeclaredFields());
        dataMap.put("classMap", classTableByApiModelProperty);
        JSONObject positionByApiModelProperty = mediaManageService.getPositionByApiModelProperty(MediaOtherMsgDTO.class.getDeclaredFields());
        dataMap.put("positionMap", positionByApiModelProperty);
        JSONObject dataTypeByApiModelProperty = mediaManageService.getDataTypeByApiModelProperty(MediaOtherMsgDTO.class.getDeclaredFields());
        dataMap.put("dataType", dataTypeByApiModelProperty);
        APPResultMessage message = APPResultMessage.getSuccessMessage();
        ResultMessage resultMessage = ResultMessage.builder().setList(mediaTypes).setData(dataMap);
        message.setResult(resultMessage);
        return message;
    }

    public APPResultMessage getMediaAppMsgList(MediaIdsReq req) {
        String[] split = req.getIds().split(",");
        List<MediaDTO> dtos = mediaMapper.selectAppMsgByIds(split);
        for (MediaDTO media : dtos){
            MediaOtherMsgDTO dto = JSONObject.parseObject(media.getOtherMsg(), MediaOtherMsgDTO.class);
            if (StringUtil.isBlank(req.getShowPassword()) || !req.getShowPassword().equals(showPassword)){
                dto.setUnitPrice("****");
                dto.setChannelName("****");
            }
            media.setOtherMsg(JSONObject.toJSONString(dto));
        }
        APPResultMessage message = APPResultMessage.getSuccessMessage();
        ResultMessage resultMessage = ResultMessage.builder().setList(dtos);
        message.setResult(resultMessage);
        return message;
    }

    public APPResultMessage getMediaAppRadiusList(MediaRadiusPageReq req) {
        String[] mediaTypes = StringUtil.isBlank(req.getMediaTypes()) ? null : req.getMediaTypes().split(";");
        String[] addresses = StringUtil.isBlank(req.getAddresses()) ? null : req.getAddresses().split(";");
        String[] numberOfMedias = StringUtil.isBlank(req.getNumberOfMedias()) ? null : req.getNumberOfMedias().split(";");
        String[] titles = StringUtil.isBlank(req.getTitles()) ? null : req.getTitles().split(";");
        List<Integer> mediaIdList = getMediaIdList(req.getLongitude(), req.getLatitude(), req.getRadius());
        if (mediaIdList == null || mediaIdList.size() == 0) {
            return APPResultMessage.getSuccessMessage();
        }
        if (req.getPageNum() != null && req.getPageSize() != null){
            PageHelper.startPage(req.getPageNum(), req.getPageSize());
            List<MediaDTO> dtos = mediaMapper.selectAppListByRadius(mediaIdList,
                    req.getKeywords(), mediaTypes, addresses, numberOfMedias, titles);
            PageInfo<MediaDTO> tables = new PageInfo<>(dtos);
            for (MediaDTO media : tables.getList()){
                MediaOtherMsgDTO dto = JSONObject.parseObject(media.getOtherMsg(), MediaOtherMsgDTO.class);
                if (StringUtil.isBlank(req.getShowPassword()) || !req.getShowPassword().equals(showPassword)){
                    dto.setUnitPrice("****");
                    dto.setChannelName("****");
                }
                media.setOtherMsg(JSONObject.toJSONString(dto));
            }
            ResultMessage result = ResultMessage.builder().setList(tables.getList()).setAllPageNumber(tables.getPages()).setCount(tables.getTotal());
            APPResultMessage<Object, MediaDTO> message = APPResultMessage.getSuccessMessage();
            message.setResult(result);
            return message;
        }else {
            List<MediaDTO> dtos = mediaMapper.selectAppListByRadius(mediaIdList,
                    req.getKeywords(), mediaTypes, addresses, numberOfMedias, titles);
            for (MediaDTO media : dtos){
                MediaOtherMsgDTO dto = JSONObject.parseObject(media.getOtherMsg(), MediaOtherMsgDTO.class);
                if (StringUtil.isBlank(req.getShowPassword()) || !req.getShowPassword().equals(showPassword)){
                    dto.setUnitPrice("****");
                    dto.setChannelName("****");
                }
                media.setOtherMsg(JSONObject.toJSONString(dto));
            }
            APPResultMessage message = APPResultMessage.getSuccessMessage();
            ResultMessage resultMessage = ResultMessage.builder().setList(dtos);
            message.setResult(resultMessage);
            return message;
        }
    }

    public APPResultMessage getMediaAppRadiusCount(MediaRadiusCountReq req) {
        // 获取在这个范围内的所有数据的id
        List<Integer> mediaIdList = getMediaIdList(req.getLongitude(), req.getLatitude(), req.getRadius());
        // 放入缓存里的数量数据
        JSONObject redisData = new JSONObject();
        String redisAllKey = req.getUuid();
        if (req.getGetType() == null || req.getGetType() == 1){
            redisAllKey = UUID.randomUUID().toString();
        }else {
            if (RedisUtil.existKey(redisAllKey)){
                redisData = RedisUtil.getCacheObject(redisAllKey);
            }else {
                for (Integer id : mediaIdList){
                    redisData.put(id.toString(), 1);
                }
            }
        }
        // 筛选条件
        String[] mediaTypes = StringUtil.isBlank(req.getMediaTypes()) ? null : req.getMediaTypes().split(";");
        String[] addresses = StringUtil.isBlank(req.getAddresses()) ? null : req.getAddresses().split(";");
        String[] numberOfMedias = StringUtil.isBlank(req.getNumberOfMedias()) ? null : req.getNumberOfMedias().split(";");
        String[] titles = StringUtil.isBlank(req.getTitles()) ? null : req.getTitles().split(";");
        Integer countType = req.getCountType() == null ? 1 : req.getCountType();
        JSONObject countMap = JSONObject.parseObject(req.getCountJsons(), JSONObject.class);
        // 返回参数
        JSONObject data = new JSONObject();
        JSONObject countReturnObject = new JSONObject();
        Integer totalNum = 0;
        BigDecimal totalPrice = new BigDecimal(0);

        List<MediaDTO> dtos = new ArrayList<>();
        if (mediaIdList == null || mediaIdList.size() == 0) {
            dtos = new ArrayList<>();
        }else {
            dtos = getBatchList(mediaIdList,
                    req.getKeywords(), mediaTypes, addresses, numberOfMedias, titles);
        }
        boolean useRedisDataBoolean = true;
        if (redisData.isEmpty()){
            useRedisDataBoolean = false;
        }
        for (MediaDTO media : dtos) {
            try {
                // 计算num
                // countType为1的时候，是单独添加数量，前端添加了数量后，后端直接使用该数据
                // countType为2的时候，是所有添加数量，前端不添加数量，传入后端的数据是当前没有批量修改前的数量，后端直接对所有的数量都进行+-1的操作
                // countType为3的时候，是所有数量-1
                // countType为10的时候，是从缓存里获取数量，不进行增减操作
                // 默认是countType为1的时候，初始的时候没有数据，所以数量都为1
                Integer num = 1;
                if (!useRedisDataBoolean){
                    num = countMap == null || countMap.getInteger(media.getId().toString()) == null ? 1 : countMap.getInteger(media.getId().toString());
                }else {
                    if (countType == 2 || countType == 3 || countType == 10){
                        num = redisData.getInteger(media.getId().toString());
                    }else {
                        num = countMap.getInteger(media.getId().toString());
                    }
                }
                if (countType == 2){
                    // countType为2的时候，数量都进行+1
                    num = num + 1;
                }else if (countType == 3){
                    num = Math.max(num - 1, 0);
                }
                MediaOtherMsgDTO dto = JSONObject.parseObject(media.getOtherMsg(), MediaOtherMsgDTO.class);
                Integer mediaNum = Integer.valueOf(dto.getNumberOfMedia()) * num;
                totalNum = totalNum + mediaNum;
                BigDecimal mediaPrice = new BigDecimal(dto.getWeeklyPricePerWeek()).multiply(new BigDecimal(String.valueOf(num)));
                totalPrice = totalPrice.add(mediaPrice);
                JSONObject returnObject = new JSONObject();
                returnObject.put("num", num);
                returnObject.put("totalAmount", mediaPrice);
                countReturnObject.put(media.getId().toString(), returnObject);
                redisData.put(media.getId().toString(), num);
            }catch (Exception e){
                System.out.println("错误数据" + media.getId());
            }
        }
        data.put("totalNum", totalNum);
        data.put("totalPrice", totalPrice);
        data.put("countReturnObject", countReturnObject);
        data.put("uuid", redisAllKey);
        // 保留1天的数据
        RedisUtil.setCacheObject(redisAllKey, redisData, 86400L, TimeUnit.SECONDS);
        APPResultMessage message = APPResultMessage.getSuccessMessage();
        ResultMessage resultMessage = ResultMessage.builder().setData(data);
        message.setResult(resultMessage);
        return message;
    }

    public List<Integer> getMediaIdList(Double longitude, Double latitude, Integer radius){
        List<Integer> idList = new ArrayList<>();
        //设置当前位置
        // // Point 中 x：经度"longitude":114.56，y：纬度"latitude":38.13
        Point point = new Point(longitude, latitude);
        //设置半径范围 (KILOMETERS 千米；METERS 米)
        Metric metric = RedisGeoCommands.DistanceUnit.KILOMETERS;
        Distance distance = new Distance(radius, metric);
        Circle circle = new Circle(point, distance);
        //设置参数 包括距离、坐标、条数
        RedisGeoCommands.GeoRadiusCommandArgs geoRadiusCommandArgs = RedisGeoCommands.GeoRadiusCommandArgs
                .newGeoRadiusArgs()
                .includeDistance()//包含距离
                .sortAscending()//正序排序
                .includeCoordinates();//包含经纬度
//                .limit(50); //条数
        GeoResults<RedisGeoCommands.GeoLocation<String>> geoResults = geoUtils.opsForGeo().radius(GeoUtils.GEO_KEY,circle, geoRadiusCommandArgs);
        if (geoResults != null) {
            Iterator<GeoResult<RedisGeoCommands.GeoLocation<String>>> iterator = geoResults.iterator();
            while (iterator.hasNext()) {
                GeoResult<RedisGeoCommands.GeoLocation<String>> geoResult = iterator.next();
                // 与目标点相距的距离信息
                Distance geoResultDistance = geoResult.getDistance();
                // 该点的信息
                RedisGeoCommands.GeoLocation<String> geoResultContent = geoResult.getContent();
                idList.add(Integer.valueOf(geoResultContent.getName()));
            }
        }
        return idList;
    }


    public APPResultMessage getMediaAppRadiusSearchList(MediaRadiusCountReq req) {
        long startTime12 = System.currentTimeMillis();
        List<Integer> mediaIdList = getMediaIdList(req.getLongitude(), req.getLatitude(), req.getRadius());
        long endTime12 = System.currentTimeMillis();
        // 返回参数
        JSONObject data = new JSONObject();
        List<String> addressList = new ArrayList<>();
        List<String> numberList = new ArrayList<>();
        List<Integer> mediaTypeList = new ArrayList<>();
        List<String> titleList = new ArrayList<>();

        long startTime = System.currentTimeMillis();
        List<MediaDTO> dtos = new ArrayList<>();
        if (mediaIdList == null || mediaIdList.size() == 0) {
            dtos = new ArrayList<>();
        }else {
            dtos = getBatchList(mediaIdList,
                    req.getKeywords(), null, null, null, null);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("时长1222======" + (endTime12 - startTime12));
        System.out.println("时长======" + (endTime - startTime));
        for (MediaDTO media : dtos) {
            MediaOtherMsgDTO dto = JSONObject.parseObject(media.getOtherMsg(), MediaOtherMsgDTO.class);
            if (!addressList.contains(media.getAddress())){
                addressList.add(media.getAddress());
            }
            if (!numberList.contains(dto.getNumberOfMedia())){
                numberList.add(dto.getNumberOfMedia());
            }
            if (!mediaTypeList.contains(media.getMediaType())){
                mediaTypeList.add(media.getMediaType());
            }
            if (!titleList.contains(media.getTitle())){
                titleList.add(media.getTitle());
            }
        }
        data.put("addressList", addressList);
        data.put("numberList", numberList);
        data.put("mediaTypeList", mediaTypeList);
        data.put("titleList", titleList);
        APPResultMessage message = APPResultMessage.getSuccessMessage();
        ResultMessage resultMessage = ResultMessage.builder().setData(data);
        message.setResult(resultMessage);
        return message;
    }




    public List<MediaExportMsgDTO> prepareUserData(MediaRadiusPageReq req) {
        String redisAllKey = req.getUuid();
        JSONObject redisData = RedisUtil.getCacheObject(redisAllKey);
        List<MediaExportMsgDTO> list = new ArrayList<>();
        String[] mediaTypes = StringUtil.isBlank(req.getMediaTypes()) ? null : req.getMediaTypes().split(";");
        String[] addresses = StringUtil.isBlank(req.getAddresses()) ? null : req.getAddresses().split(";");
        String[] numberOfMedias = StringUtil.isBlank(req.getNumberOfMedias()) ? null : req.getNumberOfMedias().split(";");
        String[] titles = StringUtil.isBlank(req.getTitles()) ? null : req.getTitles().split(";");
        List<Integer> mediaIdList = getMediaIdList(req.getLongitude(), req.getLatitude(), req.getRadius());
        if (mediaIdList == null || mediaIdList.size() == 0) {
            return list;
        }
        Integer totalNum = 0;
        BigDecimal totalPrice = new BigDecimal(0);
        Integer totalInputNum = 0;
        List<MediaDTO> dtos = getBatchList(mediaIdList,
                req.getKeywords(), mediaTypes, addresses, numberOfMedias, titles);
        for (MediaDTO media : dtos){
            MediaOtherMsgDTO dto = JSONObject.parseObject(media.getOtherMsg(), MediaOtherMsgDTO.class);
            if (StringUtil.isBlank(req.getShowPassword()) || !req.getShowPassword().equals(showPassword)){
                dto.setUnitPrice("****");
                dto.setChannelName("****");
            }
            Integer num = redisData.getInteger(media.getId().toString());
            MediaExportMsgDTO msgDTO = new MediaExportMsgDTO();
            BeanUtils.copyProperties(dto, msgDTO);
            msgDTO.setNumber(String.valueOf(num));
            list.add(msgDTO);

            Integer mediaNum = Integer.valueOf(dto.getNumberOfMedia()) * num;
            totalNum = totalNum + mediaNum;
            BigDecimal mediaPrice = new BigDecimal(dto.getWeeklyPricePerWeek()).multiply(new BigDecimal(String.valueOf(num)));
            totalPrice = totalPrice.add(mediaPrice);
            totalInputNum = totalInputNum + num;
        }
        MediaExportMsgDTO countDTO = new MediaExportMsgDTO();
        countDTO.setNumber(String.valueOf(totalInputNum));
        countDTO.setNumberOfMedia(String.valueOf(totalNum));
        countDTO.setWeeklyPricePerWeek(String.valueOf(totalPrice));
        list.add(countDTO);
        return list;
    }

    // 分批查询
    public List<MediaDTO> getBatchList(List<Integer> mediaIdList, String keywords, String[] mediaTypes, String[] addresses, String[] numberOfMedias, String[] titles){
//        return mediaMapper.selectAppListByRadius(mediaIdList, keywords, mediaTypes, addresses, numberOfMedias, titles);
        List<MediaDTO> results = Collections.synchronizedList(new ArrayList<>());
        final int BATCH_SIZE = 1000;
        ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

        List<CompletableFuture<Void>> futures = new ArrayList<>();
        for (int i = 0; i < mediaIdList.size(); i += BATCH_SIZE) {
            List<Integer> batchIds = mediaIdList.subList(i, Math.min(i + BATCH_SIZE, mediaIdList.size()));
            CompletableFuture<Void> future = CompletableFuture.supplyAsync(() ->
                            mediaMapper.selectAppListByRadius(batchIds, keywords, mediaTypes, addresses, numberOfMedias, titles), executor)
                    .thenAccept(results::addAll);
            futures.add(future);
        }

        // 等待所有异步任务完成
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
        executor.shutdown();

        return results;
    }

}
